package me.youline.dataServer.service.authentication;

import java.util.Date;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

import me.youline.dataServer.dao.UserTokenDAO;
import me.youline.dataServer.entity.UserToken;
import me.youline.dataServer.exception.UnAuthenticatedUserInfoException;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.shiro.crypto.SecureRandomNumberGenerator;
import org.perf4j.aop.Profiled;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

@Component
@Lazy(false)
public class UserTokenService {

	private static final Logger logger = LoggerFactory
			.getLogger(UserTokenService.class);

	/**
	 * token过期时间
	 */
	@Value("#{frontProperties['token.validDays']}")
	private Integer validDays;

	@Resource(name = "userTokenCompositeDaoImpl")
	private UserTokenDAO userTokenDAO;

	@PostConstruct
	private void initializeTokenCache() {
		userTokenDAO.loadAllUserToken();
	}

	/**
	 * 根据用户身份信息创建用户认证token信息
	 * 
	 * @param userId
	 * @param userName
	 * @return
	 */
	protected UserToken createUserToken(Long userId, String userName) {
		UserToken token = new UserToken();
		Date createTime = new Date();
		token.setCreateTime(createTime); 
		token.setToken(getTokenStr());
		token.setUserId(userId);
		token.setUserName(userName);
		Date expiredAt = DateUtils.addDays(createTime, validDays);
		logger.debug("[token publish] new token [{}] will expired at {} ",token.getToken(), DateFormatUtils.format(expiredAt, DateFormatUtils.ISO_DATE_FORMAT.getPattern()));
		token.setExpiredAt(expiredAt.getTime());
		return token;
	}

	/**
	 * 将用户认证token持久化存储，当前实现了在缓存中以及数据库存储
	 * 
	 * @param userToken
	 * @return
	 */
	protected UserToken saveUserToken(UserToken userToken) {
		userTokenDAO.save(userToken);
		return userToken;
	}

	/**
	 * 根据用户id和用户名颁发认证凭证
	 * 
	 * @param userBaseInfo
	 * @param userName
	 * @return
	 * @throws UnAuthenticatedUserInfoException
	 */
	@Profiled(tag="service-发布认证token")
	public UserToken publishAuthenticatedToken(Long userId, String userName)
			throws UnAuthenticatedUserInfoException {
		if (userId == null || userName == null) {
			throw new UnAuthenticatedUserInfoException("未经认证的用户信息，无法颁发认证凭证");
		}
		UserToken token = this.createUserToken(userId, userName);
		userTokenDAO.save(token);
		return token;
	}

	/**
	 * 根据token字符串，获取认证凭证数据
	 * 
	 * @param tokenStr
	 * @return
	 */
	@Profiled(tag="service-根据token串查询用户token数据")
	public UserToken getUserTokenByTokenStr(String tokenStr) {
		UserToken t = userTokenDAO.getUserTokenByTokenStr(tokenStr);
		return t;
	}

	/**
	 * 根据token字符串，取消用户认证凭证数据
	 * 
	 * @param token
	 * @return
	 */
	@Profiled(tag="service-清除token")
	public boolean clearUserTokenInfo(String token) {
		userTokenDAO.deleteUserToken(token);
		return true;
	}

	/**
	 * 根据用户名，取消用户认证凭证数据
	 * 
	 * @param userName
	 * @return
	 */
	@Profiled(tag="service-根据用户名清除token数据")
	public boolean clearUserTokenInfoByUserName(String userName) {
		UserToken t = userTokenDAO.getUserTokenByUserName(userName);
		if (t == null) {
			return true;
		}
		userTokenDAO.deleteUserToken(t.getToken());
		return true;
	}

	/**
	 * 更新token过期时间
	 * 
	 * @param token
	 * @return
	 */
	@Transactional
	@Profiled(tag="service-更新token过期时间")
	public UserToken updateTokenExpiredTime(String token) {
		UserToken t = this.getUserTokenByTokenStr(token);
		Date newExpiredTime = DateUtils.addDays(new Date(t.getExpiredAt()),
				validDays);
		userTokenDAO.updateExpiredAtTime(token, newExpiredTime);
		t.setExpiredAt(newExpiredTime.getTime());
		return t;
	}

	/**
	 * 检验当前用户身份凭证是否为合法的
	 * 
	 * @param t
	 * @return
	 */
	@Profiled(tag="service-确定当前token信息是否合法")
	public boolean isValidUserToken(UserToken t) {
		if (StringUtils.isBlank(t.getUserId().toString())
				|| StringUtils.isBlank(t.getUserName())
				|| StringUtils.isBlank(t.getToken())) {
			return false;
		}
		if (t.getExpiredAt() != null) {
			return t.getExpiredAt() < (new Date()).getTime();
		} else {
			return true;
		}
	}
	

	/**
	 * 生成认证凭证token字符串
	 * 
	 * @return
	 */
	private String getTokenStr() {
		return new SecureRandomNumberGenerator().nextBytes().toHex();
	}

	@Scheduled(fixedRate = 600000)
	@Profiled(tag="定时清除过期token")
	protected void scheduleClearExpiredToken() {
		try {
			logger.info("删除过期无效的Token");
			userTokenDAO.deleteExpiredToken(new Date());
		} catch (Exception e) {
			logger.error("删除过期token，执行失败 。信息:{}", e.getMessage(), e);
		}
	}

}
